﻿using System;
using System.Web;
using System.Web.Mvc;
using User.EF.Model;
using User.Global.Constvalues.Web;
using User.Global.Constvalues.Web.Navigation;
using User.Global.Resources;

namespace User.Web.UI.Helper.RightsManagement
{
    /// <summary>
    /// Athorization Attribut, was auswertet ob die UserRolle die übergeben wurde auf den aktellen View oder die Action zugreifen darf,
    /// dafür muss das Attribut gesetzt werden.
    /// Quellen für Filter und Attribute in ASP.NET MVC
    /// http://msdn.microsoft.com/en-us/library/gg416513%28VS.98%29.aspx
    /// http://msdn.microsoft.com/en-us/library/dd381609%28v=VS.98%29.aspx
    /// http://geekswithblogs.net/brians/archive/2010/07/08/implementing-a-custom-asp.net-mvc-authorization-filter.aspx
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        #region Member
        /// <summary>
        /// Der Aktuelle Controller der angezeigt werden soll.
        /// </summary>
        private EController Controller { get; set; }

        /// <summary>
        /// Die aktuelle Aktion die angezeigt werden soll
        /// </summary>
        private string ActionName { get; set; }

        /// <summary>
        /// Die aktuell übergebene URL Id, wenn eine übergeben wurde, sonst Null
        /// </summary>
        private string Id { get; set; }

        /// <summary>
        /// Die ID des aktuell eingeloggten Benutzers
        /// </summary>
        private long CurrentUserId { get; set; }

        private AuthorizationContext CurrentContext { get; set; }
        #endregion

        #region Konstruktor
        /// <summary>
        /// Es muss nichts übergeben werden, da controller und action direkt hier ausgelesen werden können
        /// </summary>
        public CustomAuthorizeAttribute()
        {
        }
        #endregion

        #region Public Functions
        /// <summary>
        /// Funktion die ausgeführt wird, wenn das Attribut "genutzt" wird, das Authorisation Attribut wird immer als erstes Attribut ausgeführt.
        /// </summary>
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);

            //Damit das AllowAnonymous Attribute auch "beachtet" wird, hier überprüfen ob es gesetzt ist - ging "früher" auch so aber aktuell nur so möglich.
            //Skip Authorization!
            if (filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true) || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true))
            {
                return;
            }

            //Der Filtercontext wird benötigt um auf die RequestDaten zuzugreifen, z.b. auf die UserId die zugegriffen werden soll.
            CurrentContext = filterContext;
            //Setzen des aktuellen Actionnamen, der aufgerufen wird.
            ActionName = filterContext.ActionDescriptor.ActionName;
            //Auslesen der ID, wenn eine in der URL mit übergeben wurde.
            Id = filterContext.RouteData.Values.ContainsKey("id") ? filterContext.RouteData.Values["id"].ToString() : null;
            //Den aktuellen CurrentController ermitteln, dieser ist in RouteData enthalten, des aktuellen filterContext.
            Controller = (EController)Enum.Parse(typeof(EController), filterContext.RouteData.GetRequiredString(WebConstValues.ControllerString));

            //Wenn kein User eingeloggt ist, dann hat er auch keinen Zugriff.
            if (!HttpContext.Current.User.Identity.IsAuthenticated)
            {
                filterContext.Result = new HttpUnauthorizedResult();
            }
            else
            {
                long userId;
                //Die UserId des aktuell eingeloggten Benuzers ermitteln
                if (long.TryParse(HttpContext.Current.User.Identity.Name, out userId))
                {
                    CurrentUserId = userId;
                    //Prüfen der passenden Rechte für die einzelnen Actions/Methoden
                    //Wenn der User nicht für den View/Action authentifiziert ist, dann auf die Loginseite verweisen.
                    if (!CheckRights())
                    {
                        filterContext.Controller.ViewData.ModelState.AddModelError("NoAccessRights", ErrorMessages.NoRightToAccessView);
                        //Auf die Startseite verweisen, wenn der User keinen Zugriff auf den Kontent hat, den er angefordert hat.
                        filterContext.Result = new RedirectResult(string.Format("~/{0}/{1}", EController.Home.ToString(), EActionHome.Index.ToString()));
                    }
                }
                else
                {
                    //Fehlermeldung ausgeben, wenn auf einen View zugegriffen wird, für den man eingeloggt sein muss.
                    filterContext.Controller.ViewData.ModelState.AddModelError("NotLoggedIn", ErrorMessages.NotLoggedIn);

                    //Die BenutzerID des aktuell eingeloggten Benutzters konnte NICHT ermittelt werden.
                    filterContext.Result = new RedirectResult(string.Format("~/{0}/{1}", EController.Home.ToString(), EActionHome.Index.ToString()));
                }
            }

            //Wenn alles i.o. ist "nichts" unternehmen und einfach beim Aufbau der Seite weitermachen.
        }
        #endregion

        #region Private Functions
        /// <summary>
        /// Prüfen um welchen CurrentController es sich handelt und die passende Sicherheitsprüfung vornehmen
        /// </summary>
        /// <returns>TRUE->Darf zugreifen | FALSE->Darf nicht zugreifen</returns>
        private bool CheckRights()
        {
            //Admin darf "Alles"
            if (WebRights.Rights().IsAdmin())
            {
                return true;
            }

            //Prüfen welcher CurrentController das Attribut aufgerufen hat und dann schauen welche Action aufgerufen wurde.
            switch (Controller)
            {
                case EController.Account:
                    return CheckAccountRights();

                case EController.Home:
                    return CheckHomeRights();

                case EController.Admin:
                    return CheckAdministrationRights();

                case EController.VocableModule:
                    return CheckVocableModuleRights();
            }

            return false;
        }

        /// <summary>
        /// Die AccountActions für den 
        /// </summary>
        /// <returns>TRUE->Darf zugreifen | FALSE->Darf nicht zugreifen</returns>
        private bool CheckAccountRights()
        {
            EActionAccount action;
            //Prüfen welche Aktion ausgeführt werden soll
            if (Enum.TryParse(ActionName, true, out action))
            {
                switch (action)
                {
                    //Für die Folgenden Aktionen sind keine Rechte notwendig die überprüft werden müssen.
                    case EActionAccount.Login:
                    case EActionAccount.RecoverPassword:
                    case EActionAccount.RecoverySetNewPassword:
                    case EActionAccount.Register:
                    case EActionAccount.RegisterSuccess:
                        return true;

                    //Bei den folgenden Aktionen muss überprüft werden ob es sich um den User selbst handelt, der die Änderungen durchführen möchte.
                    case EActionAccount.ChangePassword:
                    case EActionAccount.MemberEdit:
                    case EActionAccount.ProviderConnect:
                        return Id == CurrentUserId.ToString();
                }
            }

            //Wenn die Action nicht ermittelt werden konnte, dann auf die Startseite verweisen.
            return false;
        }

        /// <summary>
        /// Prüfen der HomeActions
        /// </summary>
        /// <returns>TRUE->Darf zugreifen | FALSE->Darf nicht zugreifen</returns>
        private bool CheckHomeRights()
        {
            //den passenden Enum Wert ermitteln
            EActionHome eActionHome;

            if (Enum.TryParse(ActionName, out eActionHome))
            {
                switch (eActionHome)
                {
                    case EActionHome.Index:
                        return true;
                }
            }

            //Auf alle restlichen Bereiche im Homebereich darf zugegriffen werden
            return false;
        }

        /// <summary>
        /// Prüfen der VocableModule Actions
        /// </summary>
        /// <returns>TRUE->Darf zugreifen | FALSE->Darf nicht zugreifen</returns>
        private bool CheckVocableModuleRights()
        {
            //den passenden Enum Wert ermitteln
            EActionVocableModule eActionHome;

            if (Enum.TryParse(ActionName, out eActionHome))
            {
                switch (eActionHome)
                {
                    case EActionVocableModule.Main:
                        //Nur wenn ein User für das Modul freigegeben wurde, kann er darauf zugreifen
                        return WebRights.Rights().Check(EMemberRight.VocableModule);
                }
            }

            //Auf alle restlichen Bereiche im Homebereich darf zugegriffen werden
            return false;
        }

        /// <summary>
        /// Prüfen ob der User auch auf den Adminbereich zugreifen darf, dies darf nur der SuperUser oder admin
        /// </summary>
        private bool CheckAdministrationRights()
        {
            //Man muss mindestens auf das Admin Module zugriff haben
            if (WebRights.HasRight(EMemberRight.AdminModule))
            {
                EActionAdministration eActionAdmin;
                if (Enum.TryParse(ActionName, out eActionAdmin))
                {
                    //Prüfen ob der User die passenden Rechte hat.
                    switch (eActionAdmin)
                    {
                        case EActionAdministration.MemberList:
                            return WebRights.HasRight(EMemberRight.AdminModule);

                        case EActionAdministration.MemberDetails:
                        case EActionAdministration.MemberEditAdmin:
                            return WebRights.HasRight(EMemberRight.MemberEdit);

                        case EActionAdministration.MemberRights:
                            return WebRights.HasRight(EMemberRight.MemberRights);
                    }
                }
            }

            return false;
        }
        #endregion
    }
}